﻿//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
// Exo² samples - Test
// Copyright (C) 2007-2009 Samuel Lacroix and Raphaël Legouge
//
// Distributed under GNU General Public License
// See the License folder of Exo² for details
//
//
// http://www.codeplex.com/exo2 (eng)
//-----------------------------------------------------------------------------
#include "stdafx.h"
#include "test.h"

#include "tester.h"

#include "gui.h"

                        
CTest Test;

const double CTest::EmitterRotationSpeed = 0.1;
const float CTest::PhysicsTestParticleSize = 0.01f;
const D3DXCOLOR CTest::PhysicsTestParticleColor = D3DXCOLOR(1.f, 0.1f, 0.1f, 1.f);


CTest::CTest()                 
{
        this->pDev              = NULL;
        this->DXUTExtensions    = false;
        DXUTSetCursorSettings(true, true); 
}                                     


void CTest::RegisterGameActions()                      
{                       
   /*     #define NEW_ACTION(var, name, bind) { \
                (var).reset(new Exo2::Action(name)); \
                Input().NewAction((var), std::list<tstring>(1, (bind))); }  
             
        NEW_ACTION(this->CamNewYawPos   ,   _T("yaw+")          ,   _T("mouseright")    );
        NEW_ACTION(this->CamNewYawNeg   ,   _T("yaw-")          ,   _T("mouseleft")     );
        NEW_ACTION(this->CamNewPitchPos ,   _T("pitch+")        ,   _T("mousedown")     );
        NEW_ACTION(this->CamNewPitchNeg ,   _T("pitch-")        ,   _T("mouseup")       );
        NEW_ACTION(this->CamStrafeLeft  ,   _T("strafeleft")    ,   _T("a")             );
        NEW_ACTION(this->CamStrafeRight ,   _T("straferight")   ,   _T("d")             );
        NEW_ACTION(this->CamForward     ,   _T("forward")       ,   _T("w")             );
        NEW_ACTION(this->CamBack        ,   _T("back")          ,   _T("s")             );
        NEW_ACTION(this->CamDown        ,   _T("down")          ,   _T("lshift")        );
        NEW_ACTION(this->CamUp          ,   _T("up")            ,   _T("space")         );
        NEW_ACTION(this->CamNewRollPos  ,   _T("roll+")         ,   _T("q")             );
        NEW_ACTION(this->CamNewRollNeg  ,   _T("roll-")         ,   _T("e")             );
        
        #undef NEW_ACTION    */
}
                    

void CTest::DeviceCreated()
{
        this->pDev = Exo2::CRenderer::Instance().GetD3DDevice();
        if (!this->pCam)
            this->pCam.reset(new Exo2::CCameraFPSFly);
        this->pCam->Move(0, 0, -1.);
        Exo2::CRenderer::Instance().SetCamera(this->pCam);      
}

                                              
void CTest::DeviceDestroyed()
{
        this->pDev = NULL;   
}                                        


void CTest::DeviceReset() 
{
        Exo2::CRenderer::Instance().EnableLighting(true);
        Exo2::CRenderer::Instance().EnableSpecularLighting(true);
}
                            

void CTest::DeviceLost()                 
{                       

}                                      


void CTest::RegisterConfigProperties()
{                                      
        CApplication::RegisterConfigProperties();
        /*
        this->pShipQuantity.reset(new Exo2::CTypedConfigProperty<std::size_t>(_T("ShipQuantity"), _T("Test"), _T("4")));
        Exo2::CRuntimeConfig::Instance().RegisterNewProperty(this->pShipQuantity);
        */
        if (!this->pAudioDevice)
            this->pAudioDevice.reset(new Exo2::CAudioDevice);

        pAudioDevice->RegisterConfigProperties(this->pRuntimeConfig.get());
}

void CTest::OnCreated()         
{
        tstring VersionTest = Exo2::Version::GetBuildConfiguration();
        int Version = Exo2::Version::GetMajor();
        Version = Exo2::Version::GetMinor();
        Version = Exo2::Version::GetBuild();
        Version = Exo2::Version::GetRevision();
        
        
        
        if (!this->pAudioDevice)
            this->pAudioDevice.reset(new Exo2::CAudioDevice);
            
        // Création du notifier de test
        shared_ptr<ITestNotifier> pNotifier;
        pNotifier.reset(new CPhysicsTestNotifier);

        // On passe le notifier a la class de tests
        CTester::Instance().SetNotifier(pNotifier);
            
        shared_ptr<IBasicTest> pTest;
                
        // Lecture, écriture des options
        pTest.reset(new CPhysicsOption);
        CTester::Instance().AddTest(pTest);
        
        // Test de l'utilisation des emetteurs de particules
        pTest.reset(new CPhysicsEmitter);
        CTester::Instance().AddTest(pTest);   
                                               
        // Test du manager d'objet
        pTest.reset(new CPhysicsManagement);
        CTester::Instance().AddTest(pTest);
        
        // Configuration des dispatchers
        pTest.reset(new CPhysicsDispatcher);
        CTester::Instance().AddTest(pTest);

        
        // Lancement des tests
        CTester::Instance().RunAddedTest();
        

        CreateExoTest();

    // Menu
        GUI::RegisterComponents();      // Enregistrement des composants
#ifdef _DEBUG
        Exo2::CMenuManager::Instance().RegisterComponentPropertiesInConsole = true;
#else
        Exo2::CMenuManager::Instance().RegisterComponentPropertiesInConsole = false;
#endif
        // Chargement des states sprites
        GUI::CButton::SetStateSpriteFileName(_T("data/menu/statesprites.xml"));
        GUI::CCheckBox::SetStateSpriteFileName(_T("data/menu/statesprites.xml"));
        GUI::CSlider::SetStateSpriteFileName(_T("data/menu/statesprites.xml"));
        GUI::CTextButton::SetStateSpriteFileName(_T("data/menu/statesprites.xml"));
        
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onclickgotosubmenu"),      GUI::OnClickGoToSubMenu);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onclickback"),             GUI::OnClickBack);
        
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onclickresettest"),        GUI::OnClickResetTest);
        
        // Test 1
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onclicktest1"),            GUI::OnClickTest1);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("ondblclicktest1"),         GUI::OnDblClickTest1);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onmouseentertest1"),       GUI::OnMouseEnterTest1);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onmouseleavetest1"),       GUI::OnMouseLeaveTest1);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onmousedowntest1"),        GUI::OnMouseDownTest1);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onmouseuptest1"),          GUI::OnMouseUpTest1);
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onmousemovetest1"),        GUI::OnMouseMoveTest1);
        
        // Test 2
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onclicktest2"),            GUI::OnClickTest2);
        
        // Test 3
        Exo2::CMenuManager::Instance().RegisterMenuEvent(_T("onchangetest3"),           GUI::OnChangeTest3);
        
        // Chargement du menu
        Exo2::CMenuManager::Instance().Load(_T("data/menu/menu.xml"));


        const shared_ptr<Exo2::CSpriteFont> pSF = Exo2::CResourceManager::Instance().GetResource<Exo2::CSpriteFont>(_T("data/fonts/tahoma32.ssf"));

    // test
        this->pTestText.reset(new Exo2::CSpriteText(pSF));
        this->pTestText->SetDisplayRect(Exo2::CDim2DRect(Exo2::CHorizontalDim(0, 0.), Exo2::CVerticalDim(0., 0.6), Exo2::CHorizontalDim(0, 1.), Exo2::CVerticalDim(0, 1.)));
        this->pTestText->SetTextAlignment(Exo2::ALIGN_VCENTER | Exo2::ALIGN_CENTER);
        this->pTestText->SetTextHeight(Exo2::CVerticalDim(0, 18 * .001));
        this->pTestText->SetText(_T("Lorem ipsum dolor sit amet, consectetuer adipisci elit. Illa poetis videor requirere, quippiam et autem ut et esset voluptate neque consilia sed voluptatibus est virtutum minima et, interesse exquirere et peccandi quae carere se, angere.. Versatur magnitudinem non cum esse, nominavi sine scientiam scripta bonorum.. At recteque didicisse respirare percipiatur desperantes, hoc enim omni enim, non maxime oratione omnisque persequeris modo, mutat hoc difficiles inviti sed cum epicuri vitae disseruerunt quam habent congressus, enim rationem lictores quem in viam voluptatem epicuri, qui triarius inclusae afflueret.. Labores causam nec igitur.. Intellegi disputatum, quas dolor mihi quosdam et bonum nec ullus qua idcirco tuentur iucunda dolorem est sit nati illustriora iis, quod erimus tota ratio, modo enim perpetuam.\n")
                                 _T("Ad etiam aut id et sero inertissimae panaetium, versatur ita.. Efficeretur coniuncta doloribus ad consilia, quantumcumque ex admodum sententia etiam nec corporis praestabiliorem morbos, tertium studia.. Nam vita volunt ipsa, qui carum homero maxime scientia vera quia esse, nos hoc, fama et deinde antiquis, homines praeter sophocles tamen magnus est assumenda quicquid ad deserere imagines ratione dicam est id ut quae nominavi dolore iustitia voluptas aperiam, igitur dolor aliquando esse fuisset eadem quandam, ut quoddam tam nisi sentire at ut, fastidium errata ut non scribere, distinctio quae posse recte confirmat itaque, illae.\n")
                                 _T("Maiores illum latinis ut cumque eius ad quos quid habeat quod.. Artifex sapiens docet et universas lectorem semper.. Qui esse voluptatem et voluptatibus voluptatem expeteremus, senserit cupiditates fugiendam esset desiderent non, contra consentientis qui aequitate liber omnino omne sententiam, ut consentaneum malum esse totam quietus cupiditatum nihil omnium beateque, sit quod et theophrastus.. Careat aristotele sed ea succumbere inquit, esse videretur nec conficiuntur, utilitas discordia sunt et, fungimur rationem labore isdem sensibus, tritani quo sunt quam.. Habeat omnia et praeter et facete nescio et sed sint et graece vitium civibus hic, minus conturbamur ultimum, in interdictum sabinum modo ipsa ipsi de dolores laetitiam, huc si tu unum natura, stoici etiamsi nihil, nihil aut esse fieri graecos a omne et propter voluptas.\n")
                                 _T("Dissentias a nam desiderare, non utrumque ut.. Sed et consilio, a angore bene dicemus, quid probarentur, generis futuros urbes de deterruisset, a dum, vitae sint altera distinctio rerum.. Maior nihil consequamur quod optimum quae incidunt, reperietur hostis illum multo mundus sed munere.. At recteque didicisse respirare percipiatur desperantes, hoc enim omni enim, non maxime oratione omnisque persequeris modo, mutat hoc difficiles inviti sed cum epicuri vitae disseruerunt quam habent congressus, enim rationem lictores quem in viam voluptatem epicuri, qui triarius inclusae afflueret.\n")
                                );
}                                          
                                             

void CTest::OnDestroying()                       
{
        Exo2::CMenuManager::Destroy();
        DestroyExoTest();

        this->pTestText.reset();
        this->pAudioDevice.reset();
        this->pStreamer.reset();
}                        
                                                    

void CTest::Update(double TotalTime, float ElapsedTime)
{
    // Caméra                   
/*        this->CamYaw = static_cast<float>(0.005*(this->CamNewYawPos->Value - this->CamNewYawNeg->Value));
        this->CamPitch = static_cast<float>(0.005*(this->CamNewPitchPos->Value - this->CamNewPitchNeg->Value));
        this->CamRoll = static_cast<float>(0.005*(this->CamNewRollPos->Value - this->CamNewRollNeg->Value));
        
        this->pCam->Rotate(this->CamPitch, this->CamYaw, this->CamRoll);
        D3DXVECTOR3 offset;
        offset.x = this->CamStrafeRight->Value * ElapsedTime * this->speed;
        offset.x -= this->CamStrafeLeft->Value * ElapsedTime * this->speed;
        offset.y = this->CamUp->Value * ElapsedTime * this->speed;
        offset.y -= this->CamDown->Value * ElapsedTime * this->speed;
        offset.z = this->CamForward->Value * ElapsedTime * this->speed;     
        offset.z -= this->CamBack->Value * ElapsedTime * this->speed;
        
        const D3DXVECTOR3 OldPos = this->pCam->GetPosition();
        this->pCam->Move(offset.x, offset.y, offset.z);   
        
        */

        
        UpdateExoTest(ElapsedTime, TotalTime);
        
        if (GUI::CMenuManager::Instance().IsChanged())
        {
                Exo2::CMenuManager::Instance().EnterMenu(GUI::CMenuManager::Instance().GetCurrentMenuName());
                GUI::CMenuManager::Instance().ResetChangingMenu();
        }
        Exo2::CMenuManager::Instance().Update(ElapsedTime, TotalTime);
}                       
                      

void CTest::Render3D() const
{
        EXO_DXTIF(this->pDev->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFF000000, 1.f, 0));        
        
        RenderExoTest();
               
        Exo2::CRenderer::Instance().EnableLighting(true);
        Exo2::CRenderer::Instance().EnableSpecularLighting(true);
                
    #ifdef USE_RBS
        this->pRBS->Render();
    #else
        this->pWorldMesh->Render();
        for (size_t i = 0; i < this->TestEntities.size(); ++i)
            this->TestEntities[i].pGraphicalEntity->Render();
    #endif
}                       
                                                       
                        
void CTest::Render2D() const
{ 
        this->pTestText->Render2D();
        Exo2::CMenuManager::Instance().Render2D();
}


void CTest::CreateExoTest()
{
        AudioTestCreate();
        PhysicsTestCreate();
}


void CTest::UpdateExoTest(float ElapsedTime, double TotalTime)
{
        AudioTestUpdate(ElapsedTime, TotalTime);
        PhysicsTestUpdate(ElapsedTime, TotalTime);
}

void CTest::DestroyExoTest()
{
        AudioTestDestroy();
        PhysicsTestDestroy();
}

void CTest::AudioTestCreate()
{
        Exo2::CLogger::Instance().Log(_T("Audio test created"));
                
        this->pStreamer.reset(new Exo2::CStreamer);

        // sample ogg
//         this->pSampleSource.reset(new Exo2::CSampleSource(this->pAudioDevice.get(), true, true));
//         this->pSampleSource->SetSampleFile(Exo2::CResourceManager::Instance().GetResource<Exo2::CSampleFile>(_T("sounds/sample.ogg")));
//         this->pSampleSource->SetVolume(0);
//         this->pSampleSource->Play();

        // sample wav
        this->pSampleSource2.reset(new Exo2::CSampleSource(this->pAudioDevice.get(), false, true));
        this->pSampleSource2->SetSampleFile(Exo2::CResourceManager::Instance().GetResource<Exo2::CSampleFile>(_T("sounds/sample.wav")));
        this->pSampleSource2->SetVolume(.2f);
        this->pSampleSource2->Play();
        
        // stream ogg
        this->pStreamSource.reset(new Exo2::CStreamSource(this->pAudioDevice.get(), true, true));
        this->pStreamSource->SetStreamFile(Exo2::CResourceManager::Instance().GetResource<Exo2::CStreamFile>(_T("sounds/sample.ogg")));
        this->pStreamSource->SetVolume(0.1f);
        this->pStreamer->Register(*this->pStreamSource);
        this->pStreamSource->Play();
}

void CTest::AudioTestUpdate(float /*ElapsedTime*/, double /*TotalTime*/)
{
        this->pStreamer->Update();
}

void CTest::AudioTestDestroy()
{
        this->pSampleSource.reset();
        this->pSampleSource2.reset();
        this->pStreamSource.reset();
}


void CTest::PhysicsTestCreate()
{
        Exo2::CLogger::Instance().Log(_T("Physics test created"));
        
        this->pPhysicsEngine.reset(new Exo2::CPhysicalEngine);
        
#ifdef _DEBUG
        this->pPhysicsEngine->GetLimiter().SetFactor(0.);
        this->pPhysicsEngine->SetTimeStep(1./30.);
#else
        this->pPhysicsEngine->GetLimiter().SetFactor(1.);
        this->pPhysicsEngine->SetTimeStep(1./60.);
#endif
                
        shared_ptr<Exo2::CCylinderReset> pCylinderReset;
        pCylinderReset.reset(new Exo2::CCylinderReset(Exo2::MAPI::VECTOR3(0.5, 0., 0.), Exo2::MAPI::VECTOR3(-1., 0., 0.), 0.25, 0.5, 0.05, 0.1));
        this->pEmitter.reset(new Exo2::CParticleEmitter(pCylinderReset, true, true, true, 2., 4., 100, 1000));
        this->pPhysicsEngine->GetPhysicalObjectManager().Register(this->pEmitter);
        
        shared_ptr<Exo2::CExef> pWorldExef = Exo2::CResourceManager::Instance().GetResource<Exo2::CExef>(_T("data/physics/bunny.exef"));
        
        this->pWorld.reset(new Exo2::CWorldEntity(pWorldExef, 2., Exo2::MAPI::VECTOR3(0., 0., 0.), 0., 0., 0.));
        this->pPhysicsEngine->GetPhysicalObjectManager().Register(this->pWorld);
                
        this->pRBS.reset(new Exo2::CRenderBatchingSystem);
        this->pParticleRenderer.reset(Exo2::CParticleSystem::Create<Exo2::CParticleSystem>());
        this->pParticleRenderer->SetParticleSize(CTest::PhysicsTestParticleSize);
        this->pParticleRenderer->SetParticleTexture(Exo2::CResourceManager::Instance().GetResource<Exo2::CTexture>(_T("data/graphics/particle.bmp")));
        
        shared_ptr<Exo2::CXFile> pBunnyXFile = Exo2::CResourceManager::Instance().GetResource<Exo2::CXFile>(_T("data/graphics/bunny.x"));
        Exo2::CMesh::CreationParameters C(pBunnyXFile);
        this->pWorldMesh.reset(Exo2::CMesh::Create<Exo2::CMesh>(&C));
        this->pWorldMesh->SetTransformMatrix(this->pWorld->GetPhysicTransformMatrix());
        this->pRBS->RegisterEntity(this->pWorldMesh);
        
        this->pConstraint.reset(new Exo2::CCustomEntityConstraint1(Exo2::MAPI::VECTOR3(-10, 0., 0.), Exo2::MAPI::VECTOR3(1., 0., 0.), Exo2::MAPI::VECTOR3(0., 0., 0.)));
        
        this->TestEntities.resize(CTest::TestEntityNumber);
        shared_ptr<Exo2::CXFile> pDragonpXFile = Exo2::CResourceManager::Instance().GetResource<Exo2::CXFile>(_T("data/graphics/dragon.x"));
        Exo2::CMesh::CreationParameters CP(pDragonpXFile);
        shared_ptr<Exo2::CExef> pDragonExef = Exo2::CResourceManager::Instance().GetResource<Exo2::CExef>(_T("data/physics/dragon.exef"));
        
        boost::shared_ptr<Exo2::CCustomEntityImpulse1> pImpulse;
        pImpulse.reset(new Exo2::CCustomEntityImpulse1(Exo2::MAPI::VECTOR3(-10., 0., 0.), Exo2::MAPI::VECTOR3(0.5, 0., 0.)));
        
        for (size_t i = 0; i < this->TestEntities.size(); ++i)
        {
                CTest::CTestEntity& Entity = this->TestEntities[i];
                
                Entity.pGraphicalEntity.reset(Exo2::CMesh::Create<Exo2::CMesh>(&CP));
                this->pRBS->RegisterEntity(Entity.pGraphicalEntity);
                
                Entity.pPhysicalEntity.reset(new Exo2::CEntity(pDragonExef, 0.1, Exo2::MAPI::VECTOR3(0.5, 0., 0.), 0., 0., 0.));
                //Entity.pPhysicalEntity->RegisterConstraint(this->pConstraint);
                Entity.pPhysicalEntity->RegisterConstraint(pImpulse);
                this->pPhysicsEngine->GetPhysicalObjectManager().Register(Entity.pPhysicalEntity);
        }
}


void CTest::PhysicsTestUpdate(float ElapsedTime, double TotalTime)
{
        if (Exo2::Utils::IsZero<float>(ElapsedTime))
            return;
        shared_ptr<Exo2::CCylinderReset> pCylinderReset = boost::dynamic_pointer_cast<Exo2::CCylinderReset>(this->pEmitter->GetReset());
        
        pCylinderReset->SetPosition(Exo2::MAPI::VECTOR3(0.75f * FLOAT(cos(CTest::EmitterRotationSpeed * TotalTime)), 0.f, 0.75f * FLOAT(sin(CTest::EmitterRotationSpeed * TotalTime))));
        Exo2::MAPI::VECTOR3 Dir(-1. * pCylinderReset->GetPosition().GetAbsoluteVector());
        pCylinderReset->SetDirection(Dir);
        
        this->pPhysicsEngine->Work(ElapsedTime);
        
        static double Delay = 1.;
        static double Rest = 0.;
        static std::size_t CollisionDetected = 0;
        static std::size_t CollisionSorted = 0;
        
        CollisionDetected += this->pPhysicsEngine->GetCollisionEngine().GetCollisionDetected();
        CollisionSorted += this->pPhysicsEngine->GetCollisionEngine().GetCollisionSorted();
        Rest += ElapsedTime;
        
        if (Rest > Delay)
        {
                Rest = 0.;
                tostringstream oss;
                oss << _T("Nombre de collisions : détectées ") << CollisionDetected << _T(", triées ") << CollisionSorted;
                CollisionDetected = 0;
                CollisionSorted = 0;
                Exo2::CLogger::Instance().Log(oss.str());
        }
        
        for (size_t i = 0; i < this->TestEntities.size(); ++i)
        {
                CTest::CTestEntity& Entity = this->TestEntities[i];
                
                Entity.pGraphicalEntity->SetTransformMatrix(Entity.pPhysicalEntity->GetPhysicTransformMatrix());
        }
}


void CTest::PhysicsTestDestroy()
{
        this->pPhysicsEngine.reset();
        this->pRBS.reset();
        this->pParticleRenderer.reset();
        this->pWorldMesh.reset();
        this->TestEntities.clear();
}


void CTest::RenderExoTest() const
{
        PhysicsTestRender();
}

void CTest::PhysicsTestRender() const
{                                    
        Exo2::CRenderer::Instance().EnableLighting(false);
        Exo2::CRenderer::Instance().EnableSpecularLighting(false);
                
        this->pParticleRenderer->BeginBatch();
        
        for (Exo2::IBasicEmitter::TParticleList::const_iterator ppParticle = this->pEmitter->GetParticleList().begin(); ppParticle != this->pEmitter->GetParticleList().end(); ++ppParticle)
        {
                Exo2::CParticle* pParticle = (pParticle = boost::dynamic_pointer_cast<Exo2::CParticle>(*ppParticle).get());
                EXO_ASSERT(pParticle != NULL);
                 
                this->pParticleRenderer->RenderParticle(pParticle->GetPosition(), CTest::PhysicsTestParticleColor);
                
        }
        
        this->pParticleRenderer->EndBatch();
}